package v0_catch_local_call

import (
	"context"
	"errors"
	"fmt"
	"reflect"
)

// InitClientProxy 通过反射实现为 service 的 GetById 之类的函数类型的字段"赋值"
// 这里的赋值其实就是调用Proxy的实现
func InitClientProxy(service Service) error {
	return setFuncField(service, nil)
}

func setFuncField(service Service, proxy Proxy) error {
	if service == nil {
		return errors.New("rpc: 不支持 nil")
	}
	val := reflect.ValueOf(service)
	typ := val.Type()
	// 只支持指向结构体的一级指针
	if typ.Kind() != reflect.Pointer || typ.Elem().Kind() != reflect.Struct {
		return errors.New("rpc: 只支持指向结构体的一级指针")
	}

	val = val.Elem()
	typ = typ.Elem()

	numField := typ.NumField()
	for i := 0; i < numField; i++ {
		fieldVal := val.Field(i)
		fieldTyp := typ.Field(i)
		if fieldVal.CanSet() {
			fn := func(args []reflect.Value) (results []reflect.Value) {
				ctx := args[0].Interface().(context.Context)
				req := &Request{
					ServiceName: service.Name(),
					MethodName:  fieldTyp.Name,
					Arg:         args[1].Interface(),
				}
				retVal := reflect.New(fieldTyp.Type.Out(0).Elem())
				retErrVal := reflect.Zero(reflect.TypeOf(new(error)).Elem())
				response, err := proxy.Invoke(ctx, req)
				if err != nil {
					return []reflect.Value{retVal, retErrVal}
				}

				fmt.Println(response)

				return []reflect.Value{retVal, retErrVal}
			}
			fnVal := reflect.MakeFunc(fieldTyp.Type, fn)
			fieldVal.Set(fnVal)
		}
	}
	return nil
}
